using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using BriansClimb.DrawableGameObjects.ClimbingHolds;
using BriansClimb.DrawableGameObjects.GameScenes;
using BriansClimb.GameObjects;
using BriansClimb.Climber;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;

namespace BriansClimb
{
    /// <summary>
    /// Class that implements the save/load/delete/list files functionality of the game
    /// </summary>
    public class FileManager
    {
        protected Game game;
        protected FileStream input;
        protected StreamReader fileReader;
        protected DateTime saveDateTime;

        protected BodyPart tempHold;
        protected PhysicsSimulator physicsSimulator;
        protected GameObjects.BodyPartInfoHandler bpih;

        protected List<string> listOfFiles;
        protected List<ClimbingHold> fileContentList;

        protected int fileCount;
        protected int friction;
        protected int previousFriction;
        protected string fileName;
        protected string buttonType;
        protected string difficulty;


        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="game">Main Game Object</param>
        public FileManager(Game game, PhysicsSimulator physicsSimulator)
        {
            this.game = game;
            this.physicsSimulator = physicsSimulator;
            listOfFiles = new List<string>();
            fileContentList = new List<ClimbingHold>();
            bpih = new BodyPartInfoHandler(game);
            tempHold = new BodyPart(game);
            previousFriction = 0;
        }

        /// <summary>
        /// Returns the number of save files
        /// </summary>
        public int SaveCount
        {
            get { return fileCount; }
        }

        /// <summary>
        /// Returns the list of saved files
        /// </summary>
        public List<string> FileList
        {
            get { return listOfFiles; }
        }

        /// <summary>
        /// Returns the list of climbing hold Objects from loaded file
        /// </summary>
        public List<ClimbingHold> FileContentList
        {
            get { return fileContentList; }
        }

        /// <summary>
        /// Loads a given '.wal' file
        /// </summary>
        /// <param name="nameOfFile">Name of the file to load</param>
        public void LoadFile(String nameOfFile)
        {
            ClimbingHold tempClimbingHold;
            Vector2 tempVector;
            string inputRecord;
            string[] inputFields;
            string holdType;
            int frictionValue;

            //transforms the file name to include the containing directory, and the .wal extension
            string originalFileName = @".\SaveData\" + nameOfFile + ".wal";

            //creates and opens the file
            input = new FileStream(originalFileName, FileMode.Open, FileAccess.Read);
            fileReader = new StreamReader(input);

            //make sure the list holding parsed .wal file is empty
            fileContentList.Clear();
            
            //parse the file
            while ((inputRecord = fileReader.ReadLine()) != null)
            {
                if (inputRecord != null)
                {
                    inputFields = inputRecord.Split(',');

                    tempVector.X = int.Parse(inputFields[0]);
                    tempVector.Y = int.Parse(inputFields[1]);
                    holdType = inputFields[2];
                    frictionValue = int.Parse(inputFields[3]);

                    //associating the body part and a geometry object to the climbing holdss
                    tempHold.makeBodyPart(38, 34, physicsSimulator, 
                            new FarseerGames.FarseerPhysics.Mathematics.Vector2(tempVector.X,tempVector.Y));
                    tempHold.getGeom().Collision += CollisionTest;

                    //creating a climbing hold with the parsed attributes
                    tempClimbingHold = new ClimbingHold(game, tempVector, holdType, frictionValue, tempHold);

                    //adding to the list
                    fileContentList.Add(tempClimbingHold);             
                }
            }

            //closing the file
            fileReader.Close();
            input.Close();     
        }
        
        /// <summary>
        /// Saves the coordinates of the climbing holds currently on the climbing wall
        /// </summary>
        /// <param name="climbingHoldCoordinates">Current List of climbing holds</param>
        public void SaveFile(List<ClimbingHold> climbingHoldObjects)
        {
            //Creating lists of climbing holds, coordinates and friction of the holds and button type
            List<ClimbingHold> climbingHold = climbingHoldObjects;
            List<Vector2> coordinates = new List<Vector2>();
            List<string> buttonType = new List<string>();
            List<int> friction = new List<int>();


            //Adding coordinates, button type and friction objects
            foreach (ClimbingHold ch in climbingHold)
            {
                coordinates.Add(ch.ClimbingHoldPosition);
                buttonType.Add(ch.ClimbingHoldType);
                friction.Add(ch.HoldFriction);

                //checking the highest friction in the list
                if (ch.HoldFriction > previousFriction && ch.HoldFriction < 16f)
                    previousFriction = ch.HoldFriction;
            }

            //setting the difficulty level of the route based on the highest friction level found
            if (previousFriction <= 6)
                difficulty = "Easy";
            if (previousFriction > 6 && previousFriction <= 11)
                difficulty = "Medium";
            if (previousFriction > 11 && previousFriction <= 15)
                difficulty = "Hard";

            previousFriction = 0;

            //getting current date and time based on computer
            saveDateTime = DateTime.Now;
            string dateTimePattern = @"M-d hhmmsstt";

            //transforming the filename to include the directory to save, difficulty, and .wal extension
            fileName = @".\SaveData\" + difficulty + " " + saveDateTime.ToString(dateTimePattern) + ".wal";
            
            FileInfo fileOut = new FileInfo(fileName);

            //write the text into the file
            TextWriter fileText = fileOut.CreateText();
            for (int i = 0; i < coordinates.Count; i++)
            {
                fileText.Write(coordinates[i].X);
                fileText.Write(",");
                fileText.Write(coordinates[i].Y);
                fileText.Write(",");
                fileText.Write(buttonType[i]);
                fileText.Write(",");
                fileText.Write(friction[i]);
                fileText.Write(fileText.NewLine);
            }
            fileText.Close();
        }

        /// <summary>
        /// Deletes the file
        /// </summary>
        /// <param name="fileName">Name of file to delete</param>
        /// <returns>bool value true if successful, false otherwise</returns>
        public bool DeleteFile(string fileName)
        {
            //appends to the filename the directory location, and the .wal extension
            string originalFileName = @".\SaveData\" + fileName + ".wal";
            
            try
            {
                if (File.Exists(originalFileName) == true)
                {
                    File.Delete(originalFileName);
                    return true;
                }
                else
                {
                    Console.WriteLine("File does not exist");
                    return false;
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("You do not have the required permission for this operation " +
                    ex.Message);
                return false;
            }
            catch (IOException exIO)
            {
                Console.WriteLine("File in use " + exIO.Message);
                return false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        
        }

        /// <summary>
        /// Sets the lists of files with .wal extenstion in a specified directory
        /// </summary>
        public void GetListOfFileName()
        {
            fileCount = 0;

            //Creating a reference to current directory
            DirectoryInfo dir = new DirectoryInfo(@".\SaveData\");
            
            char[] extension = new char[] { '.', 'w', 'a', 'l' };
            listOfFiles.Clear();

            //Array of FileInfo objects
            FileInfo[] fileInfo = dir.GetFiles();

            //Getting a string and adding it to ListOfFiles and checking for the files 
            //that ends with .wal
            for (int i = 0; i < fileInfo.Length; i++)
            {
                string tempFilename  = fileInfo[i].ToString();
                if (tempFilename.EndsWith(".wal"))
                {
                    fileCount++;
                    string trimmedName = tempFilename.TrimEnd(extension);
                    listOfFiles.Add(trimmedName);
                }
            }
        }

        private bool CollisionTest(Geom geometry1, Geom geometry2, ContactList contactList)
        {

            if ((geometry1.Body.IsHand) && (geometry2.Body.IsHold))
            {
                geometry1.Body.IsStatic = true;
                return false;
            }
            else if ((geometry1.Body.IsHold) && (geometry2.Body.IsHand))
            {
                geometry2.Body.IsStatic = true;
                return false;
            }
            else
                return false;
        }
    }
}